import { Vector3, Matrix } from '@babylonjs/core/Maths/math.vector';
import { Scene } from '@babylonjs/core/scene';
import { Calculation } from '../tool/calculation';
import { Buffer } from '@babylonjs/core/Buffers/buffer';
import { Geometry } from '@babylonjs/core/Meshes/geometry';
import { Mesh } from '@babylonjs/core/Meshes/mesh';

/**
 * 箭头类型
 * @param NONE 没有箭头
 * @param LEFT 起始端箭头
 * @param RIGHT 终点端箭头
 * @param DOUBLE 两端箭头
 */
enum ARROW_MODE {
    NONE,
    LEFT,
    RIGHT,
    DOUBLE
};

/**
 * 硬角线几何体参数对象
 * @param arrowLengthRatio 箭头长度（相对于虚线段长）
 * @param arrowWidthRatio 箭头宽度（相对于虚线段长）
 * @param arrowMode 箭头类型
 * @param dashSize 虚线段长度
 * @param gapSize 虚线空白长度
 */
interface IHardLineGeometryOptions {
    arrowLengthRatio?: number,
    arrowWidthRatio?: number,
    arrowMode?: ARROW_MODE,
    dashSize?: number,
    gapSize?: number
};

/**
 * 标准变换
 */
const IDENTITY_TRANSFORM = Matrix.Identity();
/**
 * 默认替换顶点集
 */
const EMPTY_VERTS = [new Vector3(0, 0, 0), new Vector3(1e-6, 0, 0)];


/**
 * 硬角线几何体（显示更完善，但拐角uv不连续，且有半透明重叠）
 * @param name 几何体id
 * @param vertices 顶点集
 * @param options 参数对象
 * @param scene 所在场景
 * @param mesh 应用到的网格
 */
class HardLineGeometry extends Geometry {

    /**
     * 是否自动更新
     */
    public autoUpdate: boolean = true;

    private _verts!: Vector3[];
    protected _maxSegments: number = 4;
    protected _vertices!: Float32Array;
    protected _nextVertices!: Float32Array;
    protected _offsets!: Float32Array;
    private _distances!: Float32Array;
    private _dashMarks!: Float32Array;
    private _rectifyVerts!: Vector3[];
    private _indexArray: number[] = [];
    private _arrowMode!: ARROW_MODE;
    private _arrowWidth!: number;
    private _arrowLength!: number;
    private _arrowWidthRatio!: number;
    private _arrowLengthRatio!: number;
    private _dashSize!: number;
    private _gapSize!: number;
    protected _tV0 = new Vector3();
    protected _tV1 = new Vector3();
    protected _tV2 = new Vector3();
    protected _tV3 = new Vector3();
    protected _tV4 = new Vector3();
    protected _tV5 = new Vector3();
    protected _tV6 = new Vector3();

    constructor(name: string, vertices: Vector3[], options?: IHardLineGeometryOptions, scene?: Scene, mesh?: Mesh) {
        super(name, scene);
        this.initialize(vertices, options, mesh);
    }

    /**
     * 获取线长度
     */
    public get length(): number {
        return this._distances[(this._rectifyVerts.length * 2 - 3) * 6 - 1];
    }

    /**
     * 设置箭头相对长度
     */
    public set arrowLengthRatio(ratio: number) {
        this._arrowLengthRatio = ratio;
        this.update();
    }

    /**
     * 获取箭头相对长度
     */
    public get arrowLengthRatio(): number {
        return this._arrowLengthRatio;
    }

    /**
     * 设置箭头相对宽度
     */
    public set arrowWidthRatio(ratio: number) {
        this._arrowWidthRatio = ratio;
        this.update();
    }

    /**
     * 获取箭头相对宽度
     */
    public get arrowWidthRatio(): number {
        return this._arrowWidthRatio;
    }

    /**
     * 设置箭头模式
     */
    public set arrowMode(mode: ARROW_MODE) {
        this._arrowMode = mode;
        this.update();
    }

    /**
     * 获取箭头模式
     */
    public get arrowMode(): ARROW_MODE {
        return this._arrowMode;
    }

    /**
     * 设置虚线段长度
     */
    public set dashSize(size: number) {
        this._dashSize = size;
        this.update();
    }

    /**
     * 获取虚线段长度
     */
    public get dashSize(): number {
        return this._dashSize;
    }

    /**
     * 设置虚线空白长度
     */
    public set gapSize(size: number) {
        this._gapSize = size;
        this.update();
    }

    /**
     * 获取虚线空白长度
     */
    public get gapSize(): number {
        return this._gapSize;
    }

    /**
     * 获取顶点集合
     */
    public get vertices(): Vector3[] {
        return this._verts;
    }

    /**
     * 设置顶点集合
     */
    public set vertices(vertices: Vector3[]) {
        this._verts = vertices;
        this.update();
    }

    /**
     * 更新线长度（顶点间的距离）
     * @param transform 要应用的变换
     */
    public updateDistance(transform?: Matrix): void {
        const matrix = transform || IDENTITY_TRANSFORM;
        const distances = this._distances;
        let d0 = 0;
        let d1 = 0;
        for (let i = 0; i < this._rectifyVerts.length - 1; i++) {
            const cp = i === 0 ? Vector3.TransformCoordinatesToRef(this._tV0.copyFrom(this._rectifyVerts[i]), matrix, this._tV0) : this._tV2.copyFrom(this._tV1);
            const np = Vector3.TransformCoordinatesToRef(this._rectifyVerts[i + 1], matrix, this._tV1);
            if (i === 0) {
                d1 = Vector3.Distance(cp, np);
                distances[0] = d0;
                distances[1] = d0;
                distances[2] = d1;
                distances[3] = d0;
                distances[4] = d1;
                distances[5] = d1;
                d0 = d1;
            } else {
                const i1 = 6 + (i - 1) * 12;
                distances[i1] = d0;
                distances[i1 + 1] = d0;
                distances[i1 + 2] = d0;
                distances[i1 + 3] = d0;
                distances[i1 + 4] = d0;
                distances[i1 + 5] = d0;

                d1 += Vector3.Distance(cp, np);
                distances[i1 + 6] = d0;
                distances[i1 + 7] = d0;
                distances[i1 + 8] = d1;
                distances[i1 + 9] = d0;
                distances[i1 + 10] = d1;
                distances[i1 + 11] = d1;
                d0 = d1;
            }
        }

        this.getVertexBuffer('lineDistance')?.update(distances);
    }

    /**
     * 更新
     */
    public update(): void {
        if (!this.autoUpdate) return;
        const len = this._verts.length;
        if (len < 2) {
            this._verts = EMPTY_VERTS;
        }
        this.generateData();
        this.getVertexBuffer('position')?.update(this._vertices);
        this.getVertexBuffer('nextPosition')?.update(this._nextVertices);
        this.getVertexBuffer('offset')?.update(this._offsets);
        this.getVertexBuffer('dashMark')?.update(this._dashMarks);
        this.updateDistance();
        this.applyIndices();
    }

    /**
     * 初始化
     * @param vertices 顶点集
     * @param options 参数对象
     * @param mesh 要应用到的网格
     */
    protected initialize(vertices: Vector3[], options?: IHardLineGeometryOptions, mesh?: Mesh): void {
        this.initializeVariable(vertices, options);
        this.generateData();
        this.setAttribute();
        this.updateDistance();
        if (mesh) this.applyToMesh(mesh);
    }

    /**
     * 初始化变量、参数等
     * @param vertices 顶点集
     * @param options 参数对象
     */
    protected initializeVariable(vertices: Vector3[], options?: IHardLineGeometryOptions): void {
        this._verts = vertices.length > 1 ? vertices : EMPTY_VERTS;
        const ops = options || {};
        this._arrowMode = ops.arrowMode || ARROW_MODE.NONE;
        this._arrowWidthRatio = ops.arrowWidthRatio || 1;
        this._arrowLengthRatio = ops.arrowLengthRatio || 4;
        this._dashSize = ops.dashSize || 0.1;
        this._gapSize = ops.gapSize || 0.1;
        this._maxSegments = this._verts.length * 2;
        const s3 = (2 * this._maxSegments - 3) * 6 * 3;
        const s1 = (2 * this._maxSegments - 3) * 6;
        this._vertices = new Float32Array(s3);
        this._nextVertices = new Float32Array(s3);
        this._offsets = new Float32Array(s1);
        this._distances = new Float32Array(s1);
        this._dashMarks = new Float32Array(s1);
    }

    /**
     * 创建几何体数据
     */
    private generateData(): void {
        this._rectifyVerts = this.rectifyPoints(this._verts);
        if (this._rectifyVerts.length < 2) {
            console.warn('At least 2 points needed!');
            return;
        }
        this.reRangeData();
        switch (this._arrowMode) {
            case ARROW_MODE.NONE:
                this.pushSolidData(this._rectifyVerts);
                break;
            case ARROW_MODE.RIGHT:
                if (this._arrowWidth === 0 || this._arrowLength === 0) {
                    this.pushSolidData(this._rectifyVerts);
                } else {
                    this.pushRightArrowData(this._rectifyVerts);
                }
                break;
            case ARROW_MODE.LEFT:
                if (this._arrowWidth === 0 || this._arrowLength === 0) {
                    this.pushSolidData(this._rectifyVerts);
                } else {
                    this.pushLeftArrowData(this._rectifyVerts);
                }
                break;
            case ARROW_MODE.DOUBLE:
                if (this._arrowWidth === 0 || this._arrowLength === 0) {
                    this.pushSolidData(this._rectifyVerts);
                } else {
                    this.pushDoubleArrowData(this._rectifyVerts);
                }
                break;
        }
    }

    /**
     * 添加数据（两端箭头时）
     * @param vs 点集
     */
    private pushDoubleArrowData(vs: Vector3[]): void {
        const n0 = this._tV0;
        const n1 = this._tV1;
        for (let i = 0; i < vs.length - 1; i++) {
            const cp = vs[i];
            const np = vs[i + 1];
            if (i === 0) {
                n0.copyFrom(np);
                n1.copyFrom(np).subtractInPlace(cp).normalize().addInPlace(np);
                this.pushData(0, 0, cp, n0, 0, 0);
                this.pushData(3, 1, cp, n0, 0, 0);
                this.pushData(6, 2, np, n1, this._arrowWidth, 0);
                this.pushData(9, 3, cp, n0, 0, 0);
                this.pushData(12, 4, np, n1, -this._arrowWidth, 0);
                this.pushData(15, 5, np, n1, this._arrowWidth, 0);
            } else if (i === vs.length - 2) {
                const i3 = 18 + (i - 1) * 36;
                const i1 = 6 + (i - 1) * 12;
                n0.copyFrom(cp).subtractInPlace(vs[i - 1]).normalize().addInPlace(cp);
                n1.copyFrom(np);
                this.pushData(i3, i1, cp, n0, 1, 1);
                this.pushData(i3 + 3, i1 + 1, cp, n0, 0, 1);
                this.pushData(i3 + 6, i1 + 2, cp, n1, 1, 1);
                this.pushData(i3 + 9, i1 + 3, cp, n0, -1, 1);
                this.pushData(i3 + 12, i1 + 4, cp, n1, -1, 1);
                this.pushData(i3 + 15, i1 + 5, cp, n1, 0, 1);

                n0.copyFrom(np);
                n1.copyFrom(np).subtractInPlace(cp).normalize().addInPlace(np);
                this.pushData(i3 + 18, i1 + 6, cp, n0, this._arrowWidth, 0);
                this.pushData(i3 + 21, i1 + 7, cp, n0, -this._arrowWidth, 0);
                this.pushData(i3 + 24, i1 + 8, np, n1, 0, 0);
                this.pushData(i3 + 27, i1 + 9, cp, n0, -this._arrowWidth, 0);
                this.pushData(i3 + 30, i1 + 10, np, n1, 0, 0);
                this.pushData(i3 + 33, i1 + 11, np, n1, 0, 0);
            } else {
                const i3 = 18 + (i - 1) * 36;
                const i1 = 6 + (i - 1) * 12;
                n0.copyFrom(cp).subtractInPlace(vs[i - 1]).normalize().addInPlace(cp);
                n1.copyFrom(np);
                this.pushData(i3, i1, cp, n0, 1, 1);
                this.pushData(i3 + 3, i1 + 1, cp, n0, 0, 1);
                this.pushData(i3 + 6, i1 + 2, cp, n1, 1, 1);
                this.pushData(i3 + 9, i1 + 3, cp, n0, -1, 1);
                this.pushData(i3 + 12, i1 + 4, cp, n1, -1, 1);
                this.pushData(i3 + 15, i1 + 5, cp, n1, 0, 1);

                n0.copyFrom(np);
                n1.copyFrom(np).subtractInPlace(cp).normalize().addInPlace(np);
                this.pushData(i3 + 18, i1 + 6, cp, n0, 1, 1);
                this.pushData(i3 + 21, i1 + 7, cp, n0, -1, 1);
                this.pushData(i3 + 24, i1 + 8, np, n1, 1, 1);
                this.pushData(i3 + 27, i1 + 9, cp, n0, -1, 1);
                this.pushData(i3 + 30, i1 + 10, np, n1, -1, 1);
                this.pushData(i3 + 33, i1 + 11, np, n1, 1, 1);
            }
        }
    }
    
    /**
     * 添加数据（起始端箭头时）
     * @param vs 点集
     */
    private pushLeftArrowData(vs: Vector3[]): void {
        const n0 = this._tV0;
        const n1 = this._tV1;
        for (let i = 0; i < vs.length - 1; i++) {
            const cp = vs[i];
            const np = vs[i + 1];
            if (i === 0) {
                n0.copyFrom(np);
                n1.copyFrom(np).subtractInPlace(cp).normalize().addInPlace(np);
                this.pushData(0, 0, cp, n0, 0, 0);
                this.pushData(3, 1, cp, n0, 0, 0);
                this.pushData(6, 2, np, n1, this._arrowWidth, 0);
                this.pushData(9, 3, cp, n0, 0, 0);
                this.pushData(12, 4, np, n1, -this._arrowWidth, 0);
                this.pushData(15, 5, np, n1, this._arrowWidth, 0);
            } else {
                const i3 = 18 + (i - 1) * 36;
                const i1 = 6 + (i - 1) * 12;
                n0.copyFrom(cp).subtractInPlace(vs[i - 1]).normalize().addInPlace(cp);
                n1.copyFrom(np);
                this.pushData(i3, i1, cp, n0, 1, 1);
                this.pushData(i3 + 3, i1 + 1, cp, n0, 0, 1);
                this.pushData(i3 + 6, i1 + 2, cp, n1, 1, 1);
                this.pushData(i3 + 9, i1 + 3, cp, n0, -1, 1);
                this.pushData(i3 + 12, i1 + 4, cp, n1, -1, 1);
                this.pushData(i3 + 15, i1 + 5, cp, n1, 0, 1);

                n0.copyFrom(np);
                n1.copyFrom(np).subtractInPlace(cp).normalize().addInPlace(np);
                this.pushData(i3 + 18, i1 + 6, cp, n0, 1, 1);
                this.pushData(i3 + 21, i1 + 7, cp, n0, -1, 1);
                this.pushData(i3 + 24, i1 + 8, np, n1, 1, 1);
                this.pushData(i3 + 27, i1 + 9, cp, n0, -1, 1);
                this.pushData(i3 + 30, i1 + 10, np, n1, -1, 1);
                this.pushData(i3 + 33, i1 + 11, np, n1, 1, 1);
            }
        }
    }
    
    /**
     * 添加数据（终点端箭头时）
     * @param vs 点集
     */
    private pushRightArrowData(vs: Vector3[]): void {
        const n0 = this._tV0;
        const n1 = this._tV1;
        for (let i = 0; i < vs.length - 1; i++) {
            const cp = vs[i];
            const np = vs[i + 1];
            if (i === 0) {
                n0.copyFrom(np);
                n1.copyFrom(np).subtractInPlace(cp).normalize().addInPlace(np);
                this.pushData(0, 0, cp, n0, 1, 1);
                this.pushData(3, 1, cp, n0, -1, 1);
                this.pushData(6, 2, np, n1, 1, 1);
                this.pushData(9, 3, cp, n0, -1, 1);
                this.pushData(12, 4, np, n1, -1, 1);
                this.pushData(15, 5, np, n1, 1, 1);
            } else if (i === vs.length - 2) {
                const i3 = 18 + (i - 1) * 36;
                const i1 = 6 + (i - 1) * 12;
                n0.copyFrom(cp).subtractInPlace(vs[i - 1]).normalize().addInPlace(cp);
                n1.copyFrom(np);
                this.pushData(i3, i1, cp, n0, 1, 1);
                this.pushData(i3 + 3, i1 + 1, cp, n0, 0, 1);
                this.pushData(i3 + 6, i1 + 2, cp, n1, 1, 1);
                this.pushData(i3 + 9, i1 + 3, cp, n0, -1, 1);
                this.pushData(i3 + 12, i1 + 4, cp, n1, -1, 1);
                this.pushData(i3 + 15, i1 + 5, cp, n1, 0, 1);

                n0.copyFrom(np);
                n1.copyFrom(np).subtractInPlace(cp).normalize().addInPlace(np);
                this.pushData(i3 + 18, i1 + 6, cp, n0, this._arrowWidth, 0);
                this.pushData(i3 + 21, i1 + 7, cp, n0, -this._arrowWidth, 0);
                this.pushData(i3 + 24, i1 + 8, np, n1, 0, 0);
                this.pushData(i3 + 27, i1 + 9, cp, n0, -this._arrowWidth, 0);
                this.pushData(i3 + 30, i1 + 10, np, n1, 0, 0);
                this.pushData(i3 + 33, i1 + 11, np, n1, 0, 0);
            } else {
                const i3 = 18 + (i - 1) * 36;
                const i1 = 6 + (i - 1) * 12;
                n0.copyFrom(cp).subtractInPlace(vs[i - 1]).normalize().addInPlace(cp);
                n1.copyFrom(np);
                this.pushData(i3, i1, cp, n0, 1, 1);
                this.pushData(i3 + 3, i1 + 1, cp, n0, 0, 1);
                this.pushData(i3 + 6, i1 + 2, cp, n1, 1, 1);
                this.pushData(i3 + 9, i1 + 3, cp, n0, -1, 1);
                this.pushData(i3 + 12, i1 + 4, cp, n1, -1, 1);
                this.pushData(i3 + 15, i1 + 5, cp, n1, 0, 1);

                n0.copyFrom(np);
                n1.copyFrom(np).subtractInPlace(cp).normalize().addInPlace(np);
                this.pushData(i3 + 18, i1 + 6, cp, n0, 1, 1);
                this.pushData(i3 + 21, i1 + 7, cp, n0, -1, 1);
                this.pushData(i3 + 24, i1 + 8, np, n1, 1, 1);
                this.pushData(i3 + 27, i1 + 9, cp, n0, -1, 1);
                this.pushData(i3 + 30, i1 + 10, np, n1, -1, 1);
                this.pushData(i3 + 33, i1 + 11, np, n1, 1, 1);
            }
        }
    }

    /**
     * 添加单条数据
     * @param datas 数据集
     */
    protected pushData(...datas: Array<number | Vector3>): void {
        const i3 = datas[0] as number;
        const i1 = datas[1] as number;
        const c = datas[2] as Vector3;
        const n = datas[3] as Vector3;
        const o = datas[4] as number;
        const dm = datas[5] as number;

        this._offsets[i1] = o;
        this._dashMarks[i1] = dm;
        this._vertices[i3] = c.x;
        this._vertices[i3 + 1] = c.y;
        this._vertices[i3 + 2] = c.z;
        this._nextVertices[i3] = n.x;
        this._nextVertices[i3 + 1] = n.y;
        this._nextVertices[i3 + 2] = n.z;
    }

    /**
     * 添加数据（无箭头时）
     * @param vs 点集
     */
    protected pushSolidData(vs: Vector3[]): void {
        const n0 = this._tV0;
        let n1 = this._tV1;
        for (let i = 0; i < vs.length - 1; i++) {
            const cp = vs[i];
            const np = vs[i + 1];
            if (i === 0) {
                n0.copyFrom(np);
                n1.copyFrom(np).subtractInPlace(cp).normalize().addInPlace(np);
                this.pushData(0, 0, cp, n0, 1, 1);
                this.pushData(3, 1, cp, n0, -1, 1);
                this.pushData(6, 2, np, n1, 1, 1);
                this.pushData(9, 3, cp, n0, -1, 1);
                this.pushData(12, 4, np, n1, -1, 1);
                this.pushData(15, 5, np, n1, 1, 1);
            } else {
                const i3 = 18 + (i - 1) * 36;
                const i1 = 6 + (i - 1) * 12;
                n0.copyFrom(cp).subtractInPlace(vs[i - 1]).normalize().addInPlace(cp);
                n1.copyFrom(np);
                this.pushData(i3, i1, cp, n0, 1, 1);
                this.pushData(i3 + 3, i1 + 1, cp, n0, 0, 1);
                this.pushData(i3 + 6, i1 + 2, cp, n1, 1, 1);
                this.pushData(i3 + 9, i1 + 3, cp, n0, -1, 1);
                this.pushData(i3 + 12, i1 + 4, cp, n1, -1, 1);
                this.pushData(i3 + 15, i1 + 5, cp, n1, 0, 1);

                n0.copyFrom(np);
                n1.copyFrom(np).subtractInPlace(cp).normalize().addInPlace(np);
                this.pushData(i3 + 18, i1 + 6, cp, n0, 1, 1);
                this.pushData(i3 + 21, i1 + 7, cp, n0, -1, 1);
                this.pushData(i3 + 24, i1 + 8, np, n1, 1, 1);
                this.pushData(i3 + 27, i1 + 9, cp, n0, -1, 1);
                this.pushData(i3 + 30, i1 + 10, np, n1, -1, 1);
                this.pushData(i3 + 33, i1 + 11, np, n1, 1, 1);
            }
        }
    }

    /**
     * 设置新的数据长度
     * @returns 数据长度是否改变
     */
    private reRangeData(): boolean {
        let reRange = this._maxSegments < this._rectifyVerts.length;
        if (reRange) {
            const s = this._rectifyVerts.length * 2;
            this._maxSegments = s;
            let s3 = (2 * s - 3) * 6 * 3;
            let s1 = (2 * s - 3) * 6;
            this.resizeDataArray(s1, s3);
            this.setAttribute();
        }
        return reRange;
    }

    /**
     * 调整数据序列大小
     * @param length1 一元数据长度
     * @param length3 三元数据长度
     */
    protected resizeDataArray(length1: number, length3: number): void {
        this._vertices = new Float32Array(length3);
        this._nextVertices = new Float32Array(length3);
        this._offsets = new Float32Array(length1);
        this._distances = new Float32Array(length1);
        this._dashMarks = new Float32Array(length1);
    }

    /**
     * 设置几何体属性
     */
    protected setAttribute(): void {
        const engine = this.getEngine();

        let bfr = this.getVertexBuffer('position');
        if (bfr) bfr.dispose();
        const verticesRefBuffer = new Buffer(engine, this._vertices, true, 3);
        this.setVerticesBuffer(verticesRefBuffer.createVertexBuffer('position', 0, 3))

        bfr = this.getVertexBuffer('nextPosition');
        if (bfr) bfr.dispose();
        const nextVerticesRefBuffer = new Buffer(engine, this._nextVertices, true, 3);
        this.setVerticesBuffer(nextVerticesRefBuffer.createVertexBuffer('nextPosition', 0, 3));

        bfr = this.getVertexBuffer('offset');
        if (bfr) bfr.dispose();
        const offsetRefBuffer = new Buffer(engine, this._offsets, true, 1);
        this.setVerticesBuffer(offsetRefBuffer.createVertexBuffer('offset', 0, 1));

        bfr = this.getVertexBuffer('dashMark');
        if (bfr) bfr.dispose();
        const dashMarkRefBuffer = new Buffer(engine, this._dashMarks, true, 1);
        this.setVerticesBuffer(dashMarkRefBuffer.createVertexBuffer('dashMark', 0, 1));

        bfr = this.getVertexBuffer('lineDistance');
        if (bfr) bfr.dispose();
        const lineDistanceRefBuffer = new Buffer(engine, this._distances, true, 1);
        this.setVerticesBuffer(lineDistanceRefBuffer.createVertexBuffer('lineDistance', 0, 1));

        this.applyIndices();
    }

    /**
     * 设置几何体索引
     */
    private applyIndices(): void {
        const indices = this._indexArray;
        indices.length = 0;
        const range = (this._rectifyVerts.length * 2 - 3) * 6;
        for (let i = 0; i < range; i++) {
            indices.push(i);
        }
        this.setIndices(indices);
    }

    /**
     * 矫正顶点集
     * @param ps 当前点集
     * @returns 矫正后的点集
     */
    protected rectifyPoints(ps: Vector3[]): Vector3[] {
        switch (this._arrowMode) {
            case ARROW_MODE.NONE:
                this._arrowWidth = 0;
                this._arrowLength = 0;
                return ps;
            case ARROW_MODE.RIGHT:
                return this.rectifyRightArrowPoints(ps);
            case ARROW_MODE.LEFT:
                return this.rectifyLeftArrowPoints(ps);
            case ARROW_MODE.DOUBLE:
                return this.rectifyDoubleArrowPoints(ps);
        }
    }

    /**
     * 矫正顶点集（两端箭头）
     * @param ps 当前点集
     * @returns 矫正后的点集
     */
    private rectifyDoubleArrowPoints(ps: Vector3[]): Vector3[] {
        let temp: Vector3[] = [];
        this._arrowWidth = 1 + 2 * this._arrowWidthRatio;
        let dAg = this._dashSize + this._gapSize;
        this._arrowLength = dAg * this._arrowLengthRatio;
        let indexLeft = -1;
        let indexRight = -1;
        let iStartLeft = -1;
        let iStartRight = -1;
        let tLen = 0;
        for (let i = 0; i < ps.length - 1; i++) {
            tLen += Vector3.Distance(ps[i], ps[i + 1]);
            if (tLen > 0) iStartLeft = i + 1;
            if (tLen >= this._arrowLength) {
                indexLeft = i + 1;
                break;
            }
        }
        tLen = 0;
        for (let i = ps.length - 1; i > 0; i--) {
            tLen += Vector3.Distance(ps[i], ps[i - 1]);
            if (tLen > 0) iStartRight = i - 1;
            if (tLen >= this._arrowLength) {
                indexRight = i - 1;
                break;
            }
        }
        if (iStartRight === -1 || iStartLeft === -1) {
            temp = ps.slice(0);
            this._arrowWidth = 0;
            this._arrowLength = 0;
        } else {
            if (indexLeft === -1 || indexRight === -1) {
                this._arrowLength = tLen;
                const dirESL = this._tV0.copyFrom(ps[iStartLeft]).subtractInPlace(ps[0]).normalize();
                const nStartL = new Vector3().copyFrom(ps[0]).addInPlace(dirESL.scaleInPlace(this._arrowLength));
                temp.push(ps[0], nStartL, nStartL);
                const dirESR = this._tV1.copyFrom(ps[iStartRight]).subtractInPlace(ps[ps.length - 1]).normalize();
                const nStartR = new Vector3().copyFrom(ps[ps.length - 1]).addInPlace(dirESR.scaleInPlace(this._arrowLength));
                temp.push(nStartR, nStartR, ps[ps.length - 1]);
            } else {
                const dpLP = new Vector3();
                const dpL = HardLineGeometry.distanceOfPointToVector(this._tV0, this._tV1, ps[0], ps[indexLeft], ps[indexLeft - 1], dpLP);
                const deL = Math.sqrt(this._arrowLength * this._arrowLength - dpL);
                const dirESL = this._tV0.copyFrom(ps[indexLeft]).subtractInPlace(ps[indexLeft - 1]).normalize();
                const nStartL = dpLP.addInPlace(dirESL.scaleInPlace(deL));

                const dpRP = new Vector3();
                const dpR = HardLineGeometry.distanceOfPointToVector(this._tV0, this._tV1, ps[ps.length - 1], ps[indexRight], ps[indexRight + 1], dpRP);
                const deR = Math.sqrt(this._arrowLength * this._arrowLength - dpR);
                const dirESR = this._tV0.copyFrom(ps[indexRight]).subtractInPlace(ps[indexRight + 1]).normalize();
                const nStartR = dpRP.addInPlace(dirESR.scaleInPlace(deR));

                temp.push(ps[0], nStartL);
                for (let i = indexLeft; i < indexRight + 1; i++) {
                    temp.push(ps[i]);
                }
                temp.push(nStartR, ps[ps.length - 1]);
            }
        }
        return temp;
    }

    /**
     * 矫正顶点集（起始端箭头）
     * @param ps 当前点集
     * @returns 矫正后的点集
     */
    private rectifyLeftArrowPoints(ps: Vector3[]): Vector3[] {
        let temp: Vector3[] = [];
        this._arrowWidth = 1 + 2 * this._arrowWidthRatio;
        let dAg = this._dashSize + this._gapSize;
        this._arrowLength = dAg * this._arrowLengthRatio;
        let index = -1;
        let iStart = -1;
        let tLen = 0;
        for (let i = 0; i < ps.length - 1; i++) {
            tLen += Vector3.Distance(ps[i], ps[i + 1]);
            if (tLen > 0) iStart = i + 1;
            if (tLen >= this._arrowLength) {
                index = i + 1;
                break;
            }
        }
        if (iStart === -1) {
            temp = ps.slice(0);
            this._arrowWidth = 0;
            this._arrowLength = 0;
        } else {
            if (index === -1) {
                this._arrowLength = tLen;
                const dirES = this._tV0.copyFrom(ps[iStart]).subtractInPlace(ps[0]).normalize();
                const nStart = new Vector3().copyFrom(ps[0]).addInPlace(dirES.scaleInPlace(this._arrowLength));
                temp.push(ps[0], nStart, nStart);
            } else {
                const dpP = new Vector3();
                const dp = HardLineGeometry.distanceOfPointToVector(this._tV0, this._tV1, ps[0], ps[index], ps[index - 1], dpP);
                const de = Math.sqrt(this._arrowLength * this._arrowLength - dp);
                const dirES = this._tV0.copyFrom(ps[index]).subtractInPlace(ps[index - 1]).normalize();
                const nStart = dpP.addInPlace(dirES.scaleInPlace(de));
                temp.push(ps[0], nStart);
                for (let i = index; i < ps.length; i++) {
                    temp.push(ps[i]);
                }
            }
        }
        return temp;
    }

    /**
     * 矫正顶点集（终点端箭头）
     * @param ps 当前点集
     * @returns 矫正后的点集
     */
    private rectifyRightArrowPoints(ps: Vector3[]): Vector3[] {
        let temp: Vector3[] = [];
        this._arrowWidth = 1 + 2 * this._arrowWidthRatio;
        const dAg = this._dashSize + this._gapSize;
        this._arrowLength = dAg * this._arrowLengthRatio;
        let index = -1;
        let iStart = -1;
        let tLen = 0;
        for (let i = ps.length - 1; i > 0; i--) {
            tLen = Vector3.Distance(ps[i], ps[i - 1]);
            if (tLen > 0) iStart = i - 1;
            if (tLen >= this._arrowLength) {
                index = i - 1;
                break;
            }
        }
        if (iStart === -1) {
            temp = ps.slice(0);
            this._arrowWidth = 0;
            this._arrowLength = 0;
        } else {
            if (index === -1) {
                this._arrowLength = tLen;
                const dirES = this._tV0.copyFrom(ps[iStart]).subtractInPlace(ps[ps.length - 1]).normalize();
                const nStart = new Vector3().copyFrom(ps[ps.length - 1]).addInPlace(dirES.scaleInPlace(this._arrowLength));
                temp.push(nStart, nStart, ps[ps.length - 1]);
            } else {
                const dpP = new Vector3();
                const dp = HardLineGeometry.distanceOfPointToVector(this._tV0, this._tV1, ps[ps.length - 1], ps[index], ps[index + 1], dpP);
                const de = Math.sqrt(this._arrowLength * this._arrowLength - dp);
                const dirES = this._tV0.copyFrom(ps[index]).subtractInPlace(ps[index + 1]).normalize();
                const nStart = dpP.addInPlace(dirES.scaleInPlace(de));
                for (let i = 0; i < index + 1; i++) {
                    temp.push(ps[i]);
                }
                temp.push(nStart, ps[ps.length - 1]);
            }
        }
        return temp;
    }

    /**
     * 点到向量的距离
     * @param dv 临时变量
     * @param de 临时变量
     * @param v 目标点
     * @param vs 向量起点
     * @param ve 向量终点
     * @param point 投影点
     * @returns 距离
     */
    private static distanceOfPointToVector(dv: Vector3, de: Vector3, v: Vector3, vs: Vector3, ve: Vector3, point: Vector3): number {
        dv.copyFrom(v).subtractInPlace(vs);
        de.copyFrom(ve).subtractInPlace(vs);
        Calculation.projectOnVector(dv, de, dv);
        dv.addInPlace(vs);
        point.copyFrom(dv);
        return Vector3.DistanceSquared(v, dv);
    }

}


export { HardLineGeometry, ARROW_MODE };
export type { IHardLineGeometryOptions }